using System;
using System.Collections;
using System.Web.UI.WebControls;
using PHSRAG.Insight21.Common.Pages;
using PHSRAG.Insight21.InsightWebControls;
using PHSRAG.Insight21.Policy;
using PHSRAG.Insight21.Purchasing.Enumerations;
using PHSRAG.Insight21.SearchContext;
using PHSRAG.Utility;
using PHSRAG.WebControls;
using Helper = PHSRAG.WebControls.Helper;
using Utils = PHSRAG.Insight21.InsightUtilities;

namespace PHSRAG.Insight21.Purchasing
{
    /// <summary>
    /// The Reconcile Orders page lists all the match and unmatched orders accessible to the current user.
    /// The users can view the unmatched/matched data relating to Purchase Orders, Journal entries, 
    /// Travel vouchers, Cheque requests and Petty cash.
    /// </summary>	
    /// <remarks>
    /// The Reconcile Orders page supports searching/filtering the user orders based on user-selectable
    /// search criteria.
    /// </remarks>
    public partial class ReconcileOrders : NoCachePage
    {
        #region Constants (Private)

        /// <summary>
        /// Index of the first control in any control hierarchy.
        /// </summary>
        private const int FirstChildControl = 0;

        /// <summary>
        /// This has to be the same as the display text for Insight data source in "Source" column of the matched Orders DataGrid
        /// </summary>
        private const string InsightDataSource = "Insight";

        private const string SearchCriteriaVersionKey = "SearchCriteriaVersion_ddffc055-1d73-4bc0-bddc-ef4517e2a12e";

        #endregion

        #region Enumerations (Private)

        #region Nested type: InsightDataGridColumnIndex

        /// <summary>
        /// Enumerates the columns in the Insight Data Grid.
        /// It has to be in sync with the columns in insightDataGrid.
        /// </summary>
        private enum InsightDataGridColumnIndex
        {
            OrderTypeID,
            OrderID,
            OrderType,
            OrderNumber,
            FundNumber,
            TotalAmount,
            RequestorName,
            Vendor,
            InvoiceNumber,
            InvoiceDate,
        }

        #endregion

        #region Nested type: MatchedOrdersDataGridColumnIndex

        /// <summary>
        /// Enumerates the columns in the Matched Orders Data Grid.
        /// It has to be in sync with the columns in matchedOrdersDataGrid.
        /// </summary>
        private enum MatchedOrdersDataGridColumnIndex
        {
            OrderTypeID,
            OrderID,
            Source,
            OrderType,
            OrderNumber,
            FundNumber,
            Amount,
            RequestorName,
            Vendor,
            InvoiceNumber,
            InvoiceDate
        }

        #endregion

        #region Nested type: PeopleSoftDataGridColumnIndex

        /// <summary>
        /// Enumerates the columns in the PeopleSoft Data Grid.
        /// It has to be in sync with the columns in peopleSoftDataGrid.
        /// </summary>
        private enum PeopleSoftDataGridColumnIndex
        {
            OrderTypeID,
            OrderID,
            OrderType,
            OrderNumber,
            FundNumber,
            TotalAmount,
            RequestorName,
            Vendor,
            InvoiceNumber,
            InvoiceDate,
        }

        #endregion

        #endregion

        #region Instance Variable (Protected)

        private ReconcileMatchedOrders matchedOrders;
        private ReconcileUnMatchedOrders unMatchedOrders;

        #endregion

        #region Instance Variables (Private)

        private ICachePolicy cachePolicy;
        private ICachePolicy pageCachePolicy;

        #endregion

        #region Private Properties

        /// <summary>
        /// Gets/sets an integer value indicating the search criteria version. 
        /// The version number changes when the search criteria changes.  This signals the page to re-build its
        /// cache.
        /// </summary>
        /// <remarks>
        /// This and all properties required to re-establish the page are stored as name / value pairs in
        /// a hash table stored in the page cache under the key Keys.FinancialSummaryPageKey.
        /// This enables the page to re-establish its state with minimal IO to what it was when
        /// the user last visited it.
        /// </remarks>
        private int SearchCriteriaVersion
        {
            get
            {
                Hashtable h = pageCachePolicy.GetCachedObject(Keys.ReconcileOrdersPageKey) as Hashtable;
                return (h == null || h[SearchCriteriaVersionKey] == null) ? 0 : (int) h[SearchCriteriaVersionKey];
            }

            set
            {
                Hashtable h = pageCachePolicy.GetCachedObject(Keys.ReconcileOrdersPageKey) as Hashtable ?? new Hashtable();
                h[SearchCriteriaVersionKey] = value;
                pageCachePolicy.Cache(Keys.ReconcileOrdersPageKey, h);
            }
        }

        /// <summary>
        /// Gets the ReconcileMatchedOrders object with its data loaded.
        /// This property calls the static method "GetCachedObject" on ReconcileMatchedOrders,
        /// to get the object from the cache. It checks if the object is null or the criteria
        /// has changed, if yes it
        /// instantiates ReconcileMatchedOrders and calls the Load method on it, otherwise
        /// it returns the object retrieved from the cache.
        /// </summary>
        private ReconcileMatchedOrders MatchedOrders
        {
            get
            {
                CommonSearchContext searchContext = (CommonSearchContext) cachePolicy.GetCachedObject(CommonSearchContext.CacheKey);
                SearchCriteria searchCriteria = searchContext.GetSearchCriteria(Page.Request.Url.ToString());
                int searchVersion = searchCriteria.Version;

                matchedOrders = cachePolicy.GetCachedObject(ReconcileMatchedOrders.CacheKey) as ReconcileMatchedOrders;
                if ((matchedOrders == null || searchVersion != SearchCriteriaVersion)
                    && searchCriteria[InsightUtilities.Constants.PurchasingSearchKey.UserOrderTypeViewKey] != null)
                {
                    SearchCriteriaVersion = searchVersion;
                    matchedOrders = new ReconcileMatchedOrders(cachePolicy);
                    matchedOrders.Load(DataAccess.GetConnectionString(),
                        ((CommonSearchContext) cachePolicy.GetCachedObject(CommonSearchContext.CacheKey)).GetSearchCriteria(Page.Request.Url.ToString()));

                    matchedOrders.Cache();
                }
                return matchedOrders ?? new ReconcileMatchedOrders(cachePolicy);
            }
        }

        /// <summary>
        /// Gets the ReconcileUnMatchedOrders object with its data loaded.
        /// This property calls the static method "GetCachedObject" on ReconcileUnMatchedOrders,
        /// to get the object from the cache. It checks if the object is null or if the criteria has
        /// changed, if yes it
        /// instantiates ReconcileUnMatchedOrders and calls the Load method on it, otherwise
        /// it returns the object retrieved from the cache.
        /// </summary>
        private ReconcileUnMatchedOrders UnMatchedOrders
        {
            get
            {
                CommonSearchContext searchContext = (CommonSearchContext) cachePolicy.GetCachedObject(CommonSearchContext.CacheKey);
                SearchCriteria searchCriteria = searchContext.GetSearchCriteria(Page.Request.Url.ToString());
                int searchVersion = searchCriteria.Version;

                unMatchedOrders = cachePolicy.GetCachedObject(ReconcileUnMatchedOrders.CacheKey) as ReconcileUnMatchedOrders;
                if ((unMatchedOrders == null || searchVersion != SearchCriteriaVersion)
                    && searchCriteria[InsightUtilities.Constants.PurchasingSearchKey.UserOrderTypeViewKey] != null)
                {
                    SearchCriteriaVersion = searchVersion;
                    unMatchedOrders = new ReconcileUnMatchedOrders(cachePolicy);
                    unMatchedOrders.Load(DataAccess.GetConnectionString(),
                        ((CommonSearchContext) cachePolicy.GetCachedObject(CommonSearchContext.CacheKey)).GetSearchCriteria(Page.Request.Url.ToString()));

                    unMatchedOrders.Cache();
                }
                return unMatchedOrders ?? new ReconcileUnMatchedOrders(cachePolicy);
            }
        }

        #endregion

        #region Event Handlers

        #region Page load

        /// <summary>
        /// Handler for the event raised when the page is being loaded. 
        /// When the page is loaded for the first time, unmatched user orders of Purchase orders are 
        /// loaded by default. On successive page requests the selected user order type, type of reconciled 
        /// orders(matched/unmatched), and other criteria used by the user the last time they visited the
        /// page will be used to populate the page. This event manages also manages display of breadcrumb 
        /// and page and when the user navigates back to this page.
        /// </summary>
        /// <param name="sender">Control which raised the event</param>
        /// <param name="e">Arguments associated with the event</param>
        protected void OnPageLoad(object sender, EventArgs e)
        {
            try
            {
                annunciator.Message = "";
                annunciator.Title = "";

                breadCrumb.Add(Constants.Reconcile, Settings.GetKeyValue
                    ("SubNavigation.Purchasing.ReconcileURL", string.Empty), Settings.GetKeyValue("BreadCrumb.SummaryPageLevel", Constants.DefaultSummaryPageLevelValue));

                purchasingSubNaviagation.SelectedSummaryLink = PurchasingSubNavigation.ReconcileCommand;
                insightDownloadBar.SQLReportName = "Reconcile";
                if (!IsPostBack)
                {
                    CommonSearchContext searchContext = cachePolicy.GetCachedObject(CommonSearchContext.CacheKey) as CommonSearchContext;

                    ((ExpansionToggler) expandingPanel.FindControl("expansionToggler")).HelpText =
                        InsightUtilities.Constants.SearchHelpText;

                    // reload the page to what it was the last time it was visited.
                    string reconcileTypeID;
                    if (searchContext != null &&
                        (reconcileTypeID = searchContext.GetSearchCriteria(Page.Request.Url.ToString())[InsightUtilities.Constants.PurchasingSearchKey.ReconcileTypeKey]) != null)
                    {
                        LoadDataGrids(Convert.ToInt32(reconcileTypeID),
                            Convert.ToInt32(searchContext.GetSearchCriteria(Page.Request.Url.ToString())[InsightUtilities.Constants.PurchasingSearchKey.UserOrderTypeViewKey]));
                    }
                    else // ask user to instigate a search
                    {
                        matchedTable.Visible = false;
                        unmatchedTable.Visible = false;
                        annunciator.Message = "Please enter criteria for orders to reconcile and click search.";
                    }
                }
            }
            catch (Exception exc)
            {
                annunciator.Message = exc.Message;
                matchedTable.Visible = false;
                unmatchedTable.Visible = false;
            }
        }

        #endregion

        #region Matched Orders

        /// <summary>
        /// Handler for the event raised when any button on data grid is clicked. 
        /// This event is used to handle linking to other pages when a link is clicked on the
        /// data grid.  If any button is clicked on the grid on an item or alternating item row, redirect to the 
        /// new page based on the type and origin of user order record.  The data associated with the click event will 
        /// be cached using the pageCachePolicy as a hash table keyed by Keys.IncomingDataKey.  Inside the hash table 
        /// are the name value pairs containing the data.
        /// </summary>
        /// <param name="source">Control which raised the event</param>
        /// <param name="e">Arguments associated with the event</param>
        private void OnMatchedOrdersDataGridItemCommand(object source, DataGridCommandEventArgs e)
        {
            string navigationURL = "";
            try
            {
                //Whenever a link is clicked, load up the name value pairs to pass to the 
                //next page and redirect to it.

                if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType ==
                    ListItemType.AlternatingItem)
                {
                    Hashtable dictionary = new Hashtable();

                    TableCellCollection cells = e.Item.Cells;

                    if (cells[(int) MatchedOrdersDataGridColumnIndex.Source].Text == InsightDataSource)
                    {
                        dictionary.Add(Keys.UserOrderIdKey, Convert.ToInt32
                            (cells[(int) MatchedOrdersDataGridColumnIndex.OrderID].Text));
                        navigationURL = Settings.GetKeyValue
                            ("SubNavigation.PurchasingDetails.UserOrderDetailsURL", string.Empty);
                    }
                    else // Source of the record is PeopleSoft tables
                    {
                        navigationURL = ManageNavigation(dictionary, cells,
                            (OrderType) Convert.ToInt32(cells[(int) MatchedOrdersDataGridColumnIndex.
                                OrderTypeID].Text), (int) MatchedOrdersDataGridColumnIndex.OrderNumber);
                    }

                    pageCachePolicy.Cache(Keys.IncomingDataKey, dictionary);
                    Response.Redirect(navigationURL, false);
                }
            }
            catch (Exception ex)
            {
                annunciator.Message = String.Format("{0} while attempting to transfer to page {1}, OrderID = {2}",
                    ExceptionReport.GetExceptionInfo(ex, false), navigationURL,
                    (e.Item.Cells[(int) MatchedOrdersDataGridColumnIndex.OrderNumber].Text));
            }
        }

        /// <summary>
        /// Handler for event when an item in MatchedOrdersDataGrid is created.
        /// OnItemCreated event of the dataGrid is used to indicate the sort column and the sort order.
        /// It references the properties on matchedOrders object for SortColumn and SortAscending and 
        /// sets the appropriate web ding in the appropriate column of the grid header as below:
        /// <list type="bullet">
        /// <item><description>Set the sort symbol text using the property of MatchedOrders object stored in cache</description></item>
        /// <item><description>Create a Hashtable of column indices, keyed by the sort expression of each column</description></item>
        /// <item><description>Using the hash table and SortColumn property on MatchedOrders object add a label 
        /// with sort symbol to the column on which sort has happened</description></item>
        /// </list>
        /// </summary>
        /// <param name="sender">Control which raised the event</param>
        /// <param name="e">Arguments associated with the event</param>
        private void OnMatchedOrdersDataGridItemCreated(object sender,
            DataGridItemEventArgs e)
        {
            if (e.Item.ItemType == ListItemType.Header)
            {
                matchedOrders = MatchedOrders;
                Label sortSymbol = new Label();
                sortSymbol.Text = matchedOrders.SortAscending ?
                                                                  InsightUtilities.Constants.SortAscendingText : InsightUtilities.Constants.SortDescendingText;
                sortSymbol.Font.Name = InsightUtilities.Constants.SortSymbolFontName;

                Hashtable columnMap = new Hashtable();
                foreach (DataGridColumn column in matchedOrdersDataGrid.Columns)
                {
                    if (column.SortExpression != string.Empty)
                    {
                        columnMap[column.SortExpression] = matchedOrdersDataGrid.Columns.IndexOf(column);
                    }
                }

                string sortColumn = matchedOrders.SortColumn;
                if (columnMap.ContainsKey(sortColumn))
                {
                    (e.Item.Cells[(int) columnMap[sortColumn]]).Controls.Add(sortSymbol);
                }
            }
            else if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
            {
                for (int i = 0, cellCount = e.Item.Cells.Count; i < cellCount; ++i)
                {
                    if ((i != (int) MatchedOrdersDataGridColumnIndex.OrderID) && (i != (int) MatchedOrdersDataGridColumnIndex.OrderTypeID)
                        && i != (int) MatchedOrdersDataGridColumnIndex.Source && i != (int) MatchedOrdersDataGridColumnIndex.OrderNumber)
                    {
                        e.Item.Cells[i].EnableViewState = false;
                    }
                }
            }
        }

        /// <summary>
        /// Handler for event when a column is sorted on the MatchedOrdersDataGrid
        /// This event handles sorting on the datagrid. If the column has already been sorted its
        /// sort order is reversed. If the columns is being sorted first time, sort order is set
        /// by default as descending.
        /// </summary>
        /// <param name="source">Control which raised the event</param>
        /// <param name="e">Arguments associated with the event</param>
        private void OnMatchedOrdersDataGridSortCommand(object source, DataGridSortCommandEventArgs e)
        {
            MatchedOrders.ProcessSortCriteria(e.SortExpression);
            CommonSearchContext searchContext = cachePolicy.GetCachedObject(CommonSearchContext.CacheKey) as CommonSearchContext;
            if (searchContext != null)
            {
                LoadDataGrids(Convert.ToInt32(searchContext.GetSearchCriteria(Page.Request.Url.ToString())[InsightUtilities.Constants.PurchasingSearchKey.ReconcileTypeKey]),
                    Convert.ToInt32(searchContext.GetSearchCriteria(Page.Request.Url.ToString())[InsightUtilities.Constants.PurchasingSearchKey.UserOrderTypeViewKey]));
            }
        }

        /// <summary>
        /// Handler for event raised when an item is data bound to MatchedOrdersDataGrid.
        /// While binding data, check if the amount field has negative values then assign a 
        /// style for displaying negative values. This formatting is applied only to Item 
        /// types and alternating Item types.
        /// </summary>
        /// <param name="sender">Control which raised the event</param>
        /// <param name="e">Argument associated with the event</param>
        private void OnMatchedOrdersDataGridItemDataBound(object sender, DataGridItemEventArgs e)
        {
            try
            {
                if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
                {
                    TableCell amount = e.Item.Cells[(int) MatchedOrdersDataGridColumnIndex.Amount];
                    decimal orderAmount = Math.Round(Convert.ToDecimal(amount.Text), 1);
                    if (orderAmount < 0)
                    {
                        amount.CssClass = Constants.DeficitStyle;
                    }
                    amount.Text = orderAmount.ToString("c", Helper.ZeroDecimals());
                }
            }
            catch (Exception ex)
            {
                if (!(ex is FormatException)) // don't want to show a message if 
                {
                    // for some reason there is no amount (and amount column = "&nbsp" or some
                    // such sequence of characters
                    annunciator.Message = String.Format("{0} Error while attempting to format amount.",
                        ExceptionReport.GetExceptionInfo(ex, false));
                }
            }
        }

        #endregion matched orders

        #region Insight Orders

        /// <summary>
        /// Handler for the event raised when any button on data grid is clicked.
        /// This event is used to handle linking to other pages when a link is clicked on the
        /// data Grid. If any link control was clicked on the grid on an item or alternating item row, redirect to the 
        /// new page that is specified in the CommandName property. The data associated with the click event will 
        /// be cached using the pageCachePolicy as a hash table keyed by Keys.IncomingDataKey.  Inside the hash table 
        /// are the name value pairs containing the data.
        /// </summary>
        /// <param name="source">Control which raised the event</param>
        /// <param name="e">Arguments associated with the event</param>
        private void OnInsightDataGridItemCommand(object source, DataGridCommandEventArgs e)
        {
            try
            {
                //Whenever a link is clicked, load up the name value pairs to pass to the 
                //next page and redirect to it.

                if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType ==
                    ListItemType.AlternatingItem)
                {
                    Hashtable dictionary = new Hashtable();
                    dictionary.Add(Keys.UserOrderIdKey, Convert.ToInt32(e.Item.Cells[(int) InsightDataGridColumnIndex.OrderID].Text.Trim()));
                    pageCachePolicy.Cache(Keys.IncomingDataKey, dictionary);
                    PurgeCache();
                    Response.Redirect(e.CommandName, false);
                }
            }
            catch (Exception ex)
            {
                annunciator.Message = String.Format(@"{0} while attempting to transfer to page {1}, OrderID = {2}",
                    ExceptionReport.GetExceptionInfo(ex, false), e.CommandName,
                    e.Item.Cells[(int) InsightDataGridColumnIndex.OrderNumber].Text);
            }
        }

        /// <summary>
        /// Handler for event when an item in InsightDataGridItem is created.
        /// OnItemCreated event of the dataGrid is used to indicate the sort column and the sort order.
        /// It references the properties on UnMatchedOrders for SortColumn and SortAscending and sets 
        /// the appropriate webding in the appropriate column of the grid header as below:
        /// <list type="bullet">
        /// <item><description>Set the sort symbol text using the property of UnMatchedOrders object stored in cache</description></item>
        /// <item><description>Create a Hashtable of column indices, keyed by the sort expression of each column</description></item>
        /// <item><description>Using the hash table and SortColumn property on UnMatchedOrders object add a label 
        /// with sort symbol to the column on which sort has happened</description></item>
        /// </list>
        /// </summary>
        /// <param name="sender">Control which raised the event</param>
        /// <param name="e">Arguments associated with the event</param>
        private void OnInsightDataGridItemCreated(object sender, DataGridItemEventArgs e)
        {
            if (e.Item.ItemType == ListItemType.Header)
            {
                unMatchedOrders = UnMatchedOrders;

                Label sortSymbol = new Label();
                sortSymbol.Text = unMatchedOrders.InsightSortAscending ?
                                                                           InsightUtilities.Constants.SortAscendingText : InsightUtilities.Constants.SortDescendingText;
                sortSymbol.Font.Name = InsightUtilities.Constants.SortSymbolFontName;

                Hashtable columnMap = new Hashtable();
                foreach (DataGridColumn column in insightDataGrid.Columns)
                {
                    if (column.SortExpression != string.Empty)
                    {
                        columnMap[column.SortExpression] = insightDataGrid.Columns.IndexOf(column);
                    }
                }

                string sortColumn = unMatchedOrders.InsightSortColumn;
                if (columnMap.ContainsKey(sortColumn))
                {
                    (e.Item.Cells[(int) columnMap[sortColumn]]).Controls.Add(sortSymbol);
                }
            }
            else if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
            {
                for (int i = 0, cellCount = e.Item.Cells.Count; i < cellCount; ++i)
                {
                    if (i != (int) InsightDataGridColumnIndex.OrderID)
                    {
                        e.Item.Cells[i].EnableViewState = false;
                    }
                }
            }
        }

        /// <summary>
        /// Handler for event when any column on InsightDataGrid is sorted.
        /// This event handles sorting on the datagrid. If the column has already been sorted its
        /// sort order is reversed. If the columns is being sorted first time, sort order is set
        /// by default as descending.
        /// </summary>
        /// <param name="source">Control which raised the event</param>
        /// <param name="e">Arguments associated with the event</param>
        private void OnInsightDataGridSortCommand(object source, DataGridSortCommandEventArgs e)
        {
            UnMatchedOrders.ProcessSortCriteria(e.SortExpression,
                Constants.ReconcileUnMatchedInsightTableName);
            CommonSearchContext searchContext = cachePolicy.GetCachedObject(CommonSearchContext.CacheKey) as CommonSearchContext;
            if (searchContext != null)
            {
                LoadDataGrids(Convert.ToInt32(searchContext.GetSearchCriteria(Page.Request.Url.ToString())[InsightUtilities.Constants.PurchasingSearchKey.ReconcileTypeKey]),
                    Convert.ToInt32(searchContext.GetSearchCriteria(Page.Request.Url.ToString())[InsightUtilities.Constants.PurchasingSearchKey.UserOrderTypeViewKey]));
            }
        }

        /// <summary>
        /// Handler for event raised when an item is data bound to InsightDataGrid.
        /// While binding data check if the amount field has negative values then assign a 
        /// style for displaying negative values. This formatting  is applied only to Item 
        /// types and alternating Item types.
        /// </summary>
        /// <param name="sender">Control which raised the event</param>
        /// <param name="e">Argument associated with the event</param>
        private void OnInsightDataGridItemDataBound(object sender, DataGridItemEventArgs e)
        {
            try
            {
                if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
                {
                    TableCell amount = e.Item.Cells[(int) InsightDataGridColumnIndex.TotalAmount];
                    decimal orderAmount = Math.Round(Convert.ToDecimal(amount.Text), 1);
                    if (orderAmount < 0)
                    {
                        amount.CssClass = Constants.DeficitStyle;
                    }
                    amount.Text = orderAmount.ToString("c", Helper.ZeroDecimals());
                }
            }
            catch (Exception ex)
            {
                annunciator.Message = String.Format("{0} Error while attempting to format total amount.",
                    ExceptionReport.GetExceptionInfo(ex, false));
            }
        }

        #endregion insight Orders

        #region PeopleSoft Orders

        /// <summary>
        /// Handler for the event raised when any button on data grid is clicked.
        /// This event is used to handle linking to other pages when a link is clicked on the data Grid. 
        /// If any link control was clicked on the grid on an item or alternating item row, redirect to
        /// new page based on the type of the user order.  The data associated with the click event will 
        /// be cached using the pageCachePolicy as a hash table keyed by Keys.IncomingDataKey.  Inside 
        /// the hash table are the name value pairs containing the data.
        /// </summary>
        /// <param name="source">Control which raised the event</param>
        /// <param name="e">Arguments associated with the event</param>
        private void OnPeopleSoftDataGridItemCommand(object source, DataGridCommandEventArgs e)
        {
            string navigationURL = null;
            try
            {
                //Whenever a link is clicked, load up the name value pairs to pass to 
                //the next page and redirect to it.

                if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType ==
                    ListItemType.AlternatingItem)
                {
                    Hashtable dictionary = new Hashtable();

                    TableCellCollection cells = e.Item.Cells;

                    navigationURL = ManageNavigation(dictionary, cells, (OrderType) Convert.ToInt32
                        (cells[(int) PeopleSoftDataGridColumnIndex.OrderTypeID].Text),
                        (int) PeopleSoftDataGridColumnIndex.OrderNumber);

                    pageCachePolicy.Cache(Keys.IncomingDataKey, dictionary);
                    PurgeCache();
                    Response.Redirect(navigationURL, false);
                }
            }
            catch (Exception ex)
            {
                annunciator.Message = String.Format("{0} while attempting to transfer to page {1}, OrderNumber = {2}",
                    ExceptionReport.GetExceptionInfo(ex, false), navigationURL,
                    ((LinkButton) e.Item.Cells[1].Controls[FirstChildControl]).Text);
            }
        }

        /// <summary>
        /// Handler for event when any item in PeopleSoftDataGrid is created.
        /// On Item Created event of the dataGrid is used to indicate the sort column and the sort order.
        /// It references the properties on UnMatchedOrders for SortColumn and SortAscending and sets
        /// the appropriate web ding in the appropriate column of the grid header as below.
        /// <list type="bullet">
        /// <item><description>Set the sort symbol text using the property of UnMatchedOrders object stored in cache</description></item>
        /// <item><description>Create a Hashtable of column indices, keyed by the sort expression of each column</description></item>
        /// <item><description>Using the hash table and SortColumn property on UnMatchedOrders object add a label 
        /// with sort symbol to the column on which sort has happened</description></item>
        /// </list>
        /// </summary>
        /// <param name="sender">Control which raised the event</param>
        /// <param name="e">Arguments associated with the event</param>
        private void OnPeopleSoftDataGridItemCreated(object sender, DataGridItemEventArgs e)
        {
            if (e.Item.ItemType == ListItemType.Header)
            {
                unMatchedOrders = UnMatchedOrders;

                Label sortSymbol = new Label();
                sortSymbol.Text = unMatchedOrders.PeopleSoftSortAscending ?
                                                                              InsightUtilities.Constants.SortAscendingText : InsightUtilities.Constants.SortDescendingText;
                sortSymbol.Font.Name = InsightUtilities.Constants.SortSymbolFontName;

                Hashtable columnMap = new Hashtable();
                foreach (DataGridColumn column in peopleSoftDataGrid.Columns)
                {
                    if (column.SortExpression != string.Empty)
                    {
                        columnMap[column.SortExpression] = peopleSoftDataGrid.Columns.IndexOf(column);
                    }
                }

                string sortColumn = unMatchedOrders.PeopleSoftSortColumn;
                if (columnMap.ContainsKey(sortColumn))
                {
                    (e.Item.Cells[(int) columnMap[sortColumn]]).Controls.Add(sortSymbol);
                }
            }
            else if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
            {
                for (int i = 0, cellCount = e.Item.Cells.Count; i < cellCount; ++i)
                {
                    if (i != (int) PeopleSoftDataGridColumnIndex.OrderTypeID &&
                        i != (int) PeopleSoftDataGridColumnIndex.OrderNumber)
                    {
                        e.Item.Cells[i].EnableViewState = false;
                    }
                }
            }
        }

        /// <summary>
        /// Handler for event when any column on PeopleSoftDataGrid is sorted.
        /// This event handles sorting on the datagrid. If the column has already been sorted its
        /// sort order is reversed. If the columns is being sorted first time, sort order is set
        /// by default as descending.
        /// </summary>
        /// <param name="source">Control which raised the event</param>
        /// <param name="e">Arguments associated with the event</param>
        private void OnPeopleSoftDataGridSortCommand(object source, DataGridSortCommandEventArgs e)
        {
            UnMatchedOrders.ProcessSortCriteria(e.SortExpression,
                Constants.ReconcileUnMatchedPSTableName);
            CommonSearchContext searchContext = cachePolicy.GetCachedObject(CommonSearchContext.CacheKey) as CommonSearchContext;
            if (searchContext != null)
            {
                LoadDataGrids(Convert.ToInt32(searchContext.GetSearchCriteria(Page.Request.Url.ToString())[InsightUtilities.Constants.PurchasingSearchKey.ReconcileTypeKey]),
                    Convert.ToInt32(searchContext.GetSearchCriteria(Page.Request.Url.ToString())[InsightUtilities.Constants.PurchasingSearchKey.UserOrderTypeViewKey]));
            }
        }

        /// <summary>
        /// Handler for event raised when an item is data bound to PeopleSoftDataGrid.
        /// While binding data check if the amount field has negative values then assign a 
        /// style for displaying negative values. This formatting  is applied only to Item 
        /// types and alternating Item types.
        /// </summary>
        /// <param name="sender">Control which raised the event</param>
        /// <param name="e">Argument associated with the event</param>
        private void OnPeopleSoftDataGridItemDataBound(object sender, DataGridItemEventArgs e)
        {
            try
            {
                if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
                {
                    TableCell amount = e.Item.Cells[(int) PeopleSoftDataGridColumnIndex.TotalAmount];
                    decimal orderAmount = Math.Round(Convert.ToDecimal(amount.Text), 1);
                    if (orderAmount < 0)
                    {
                        amount.CssClass = Constants.DeficitStyle;
                    }
                    amount.Text = orderAmount.ToString("c", Helper.ZeroDecimals());
                }
            }
            catch (Exception ex)
            {
                annunciator.Message = String.Format("{0} Error while attempting to format total amount.",
                    ExceptionReport.GetExceptionInfo(ex, false));
            }
        }

        #endregion

        #region Downloads

        /// <summary>
        /// Invoked by the download bar when it catches an exception.
        /// </summary>
        /// <param name="sender">Control which raised the event</param>
        /// <param name="e">Argument associated with the event</param>
        private void OnDownloadFailed(object sender, CommandEventArgs e)
        {
            annunciator.Title = "Following error(s) occurred while downloading";
            annunciator.Message = ExceptionReport.GetExceptionInfo((Exception) e.CommandArgument, false);
        }

        #endregion

        #region Search event handler

        /// <summary>
        /// Handler for the event raised by the CommonSearch control when the "Clear Selection" button is clicked upon.
        /// The version for the search criteria is incremented to sync up with the version maintained by the
        /// SearchCriteria object, datagrids are rendered invisible and the user is prompted to instigate a search
        /// after entering desired criteria.
        /// </summary>
        /// <param name="sender">Sender of the event</param>
        /// <param name="e">Argument associated with the event</param>
        protected void OnClearSelection(object sender, CommandEventArgs e)
        {
            ++SearchCriteriaVersion;
            matchedTable.Visible = false;
            unmatchedTable.Visible = false;
            annunciator.Message = "Please enter criteria for orders to reconcile and click search.";
        }

        /// <summary>
        /// Handler for the event raised when the Search button is clicked.
        /// Gets the search criteria and performs a search. The actual search is performed by the Reconcile objects
        /// (depending on the recocile type selected by the user. The version for the search criteria
        /// maintained by the page is also incremented to macth the version number maintained by the
        /// SearchCriteria object when search is instigated for the first time (version is -1).
        /// </summary>
        /// <param name="args">Search Criteria args associated with the search event</param>
        protected void OnSearch(SearchCriteriaArgs args)
        {
            try
            {
                if (SearchCriteriaVersion == -1)
                {
                    ++SearchCriteriaVersion;
                }

                CommonSearchContext searchContext = cachePolicy.GetCachedObject(CommonSearchContext.CacheKey) as CommonSearchContext;
                if (searchContext != null)
                {
                    LoadDataGrids(Convert.ToInt32(searchContext.GetSearchCriteria(Page.Request.Url.ToString())[InsightUtilities.Constants.PurchasingSearchKey.ReconcileTypeKey]),
                        Convert.ToInt32(searchContext.GetSearchCriteria(Page.Request.Url.ToString())[InsightUtilities.Constants.PurchasingSearchKey.UserOrderTypeViewKey]));
                }
            }
            catch (Exception exc)
            {
                annunciator.Message = exc.Message;
                matchedTable.Visible = false;
                unmatchedTable.Visible = false;
            }
        }

        #endregion

        #region Header

        /// <summary>
        /// Resets the subnavigation selected link(s) before the 
        /// header performs a redirect.
        /// </summary>
        /// <param name="sender">Control which rasied the event.</param>
        /// <param name="e">Arguments associated with the event.</param>
        private void OnHeaderNavigate(object sender, CommandEventArgs e)
        {
            purchasingSubNaviagation.Reset();
            PurgeCache();
        }

        #endregion

        #region Criteria Display Handlers

        /// <summary>
        /// Fires when the criteria display tries to display the search criteria on the page.
        /// </summary>
        /// <param name="criteriaKey">The criteria key.</param>
        /// <returns>Whether or not to display.</returns>
        private bool OnDisplayCriteria(string criteriaKey)
        {
            string criteriaKeyLowerCase = criteriaKey.ToLower();
            return criteriaKeyLowerCase == InsightUtilities.Constants.PIIDKey ||
                criteriaKeyLowerCase == InsightUtilities.Constants.CosKey ||
                    criteriaKeyLowerCase == InsightUtilities.Constants.PurchasingSearchKey.PONumberKey ||
                        criteriaKeyLowerCase == InsightUtilities.Constants.PurchasingSearchKey.VendorNameKey ||
                            criteriaKeyLowerCase == InsightUtilities.Constants.PurchasingSearchKey.InvoiceNumberKey ||
                                criteriaKeyLowerCase == InsightUtilities.Constants.PurchasingSearchKey.RequestorNameKey ||
                                    criteriaKeyLowerCase == InsightUtilities.Constants.PurchasingSearchKey.DaysTransactionsWithinKey ||
                                        criteriaKeyLowerCase == InsightUtilities.Constants.PurchasingSearchKey.InvoiceDateKey ||
                                            criteriaKeyLowerCase == InsightUtilities.Constants.PurchasingSearchKey.TotalOrderAmountKey ||
                                                criteriaKeyLowerCase == InsightUtilities.Constants.PurchasingSearchKey.TransactionDateFromKey ||
                                                    criteriaKeyLowerCase == InsightUtilities.Constants.PurchasingSearchKey.TransactionDateToKey ||
                                                        criteriaKeyLowerCase == InsightUtilities.Constants.FundIDKey ||
                                                            criteriaKeyLowerCase == InsightUtilities.Constants.PurchasingSearchKey.UserOrderTypeViewKey ||
                                                                criteriaKeyLowerCase == InsightUtilities.Constants.PurchasingSearchKey.ReconcileTypeKey ? true : false;
        }

        #endregion

        #endregion

        #region Private Methods

        /// <summary>
        /// Loads the data grid with reconcile data based on the reconcile type 
        /// and order type; sorted and filtered according to the options set by the user.
        /// </summary>
        /// <param name="reconcileTypeID">Whether to display matched or umatched
        /// records</param>
        /// <param name="orderTypeID">The type of order being displayed.</param>
        private void LoadDataGrids(int reconcileTypeID, int orderTypeID)
        {
            try
            {
                // If user has selected to view reconcile matched data
                if (reconcileTypeID == (int) ReconcileType.Matched)
                {
                    unmatchedTable.Visible = false;
                    matchedTable.Visible = true;

                    // display the columns as per the order type selected
                    ManageMatchedGridDisplay(orderTypeID);

                    matchedOrdersDataGrid.DataSource = MatchedOrders.Sort();
                    matchedOrdersDataGrid.DataBind();

                    if (matchedOrdersDataGrid.Items.Count == 0)
                    {
                        annunciator.Message = "No Data found for Matched Orders.";
                        matchedTable.Visible = false;
                    }
                }
                else
                {
                    matchedTable.Visible = false;
                    unmatchedTable.Visible = true;

                    // display the columns based on order type selected
                    ManageUnmatchedGridsDisplay(orderTypeID);

                    insightDataGrid.DataSource = UnMatchedOrders.Sort(Constants.ReconcileUnMatchedInsightTableName);
                    insightDataGrid.DataBind();

                    bool displayInsightDataGrid = insightDataGrid.Items.Count != 0;

                    if (!displayInsightDataGrid)
                    {
                        annunciator.Message = "No Data found for Insight Transactions.";
                    }

                    peopleSoftDataGrid.DataSource = UnMatchedOrders.Sort(Constants.ReconcileUnMatchedPSTableName);
                    peopleSoftDataGrid.DataBind();

                    bool displayPSDataGrid = peopleSoftDataGrid.Items.Count != 0;

                    if (!displayPSDataGrid)
                    {
                        annunciator.Message = "No Data found for PeopleSoft Transactions.";
                    }

                    if (!displayInsightDataGrid && !displayPSDataGrid)
                    {
                        annunciator.Message = "No Data found for both PeopleSoft and Insight Transactions.";
                        unmatchedTable.Visible = false;
                    }
                }
            }
            catch (Exception e)
            {
                annunciator.Message = String.Format("{0}. Error while loading data on grids.",
                    ExceptionReport.GetExceptionInfo(e, false));
                matchedTable.Visible = false;
                unmatchedTable.Visible = false;
            }
        }

        /// <summary>
        /// This method toggles the visible property of columns in peoplesoft data grid and 
        /// insight datagrid depending on the user order type selected. Invoice number
        /// and Invoice Date columns are displayed for Travel Voucher and Check Requests
        /// order types only. Vendor column is not available to JE and PC.
        /// </summary>
        /// <param name="orderTypeID">Order type for orders being displayed.</param>
        private void ManageUnmatchedGridsDisplay(int orderTypeID)
        {
            switch ((OrderType) orderTypeID)
            {
                case OrderType.PurchaseOrder:
                    insightDataGrid.Columns[(int) InsightDataGridColumnIndex.InvoiceNumber].Visible =
                        peopleSoftDataGrid.Columns[(int) PeopleSoftDataGridColumnIndex.InvoiceNumber].Visible = false;
                    insightDataGrid.Columns[(int) InsightDataGridColumnIndex.InvoiceDate].Visible =
                        peopleSoftDataGrid.Columns[(int) PeopleSoftDataGridColumnIndex.InvoiceDate].Visible = false;
                    insightDataGrid.Columns[(int) InsightDataGridColumnIndex.Vendor].Visible =
                        peopleSoftDataGrid.Columns[(int) PeopleSoftDataGridColumnIndex.Vendor].Visible = true;
                    break;

                case OrderType.JournalEntry:
                case OrderType.PettyCash:
                    insightDataGrid.Columns[(int) InsightDataGridColumnIndex.InvoiceNumber].Visible =
                        peopleSoftDataGrid.Columns[(int) PeopleSoftDataGridColumnIndex.InvoiceNumber].Visible = false;
                    insightDataGrid.Columns[(int) InsightDataGridColumnIndex.InvoiceDate].Visible =
                        peopleSoftDataGrid.Columns[(int) PeopleSoftDataGridColumnIndex.InvoiceDate].Visible = false;
                    insightDataGrid.Columns[(int) InsightDataGridColumnIndex.Vendor].Visible =
                        peopleSoftDataGrid.Columns[(int) PeopleSoftDataGridColumnIndex.Vendor].Visible = false;
                    break;

                case OrderType.TravelVoucher:
                case OrderType.ChequeRequest:
                    insightDataGrid.Columns[(int) InsightDataGridColumnIndex.InvoiceNumber].Visible =
                        peopleSoftDataGrid.Columns[(int) PeopleSoftDataGridColumnIndex.InvoiceNumber].Visible = true;
                    insightDataGrid.Columns[(int) InsightDataGridColumnIndex.InvoiceDate].Visible =
                        peopleSoftDataGrid.Columns[(int) PeopleSoftDataGridColumnIndex.InvoiceDate].Visible = true;
                    insightDataGrid.Columns[(int) InsightDataGridColumnIndex.Vendor].Visible =
                        peopleSoftDataGrid.Columns[(int) PeopleSoftDataGridColumnIndex.Vendor].Visible = true;
                    break;

                default:
                    break;
            }
        }

        /// <summary>
        /// This method toggles the visible property of columns in matched orders
        /// datagrid depending on the user order type selected. The Invoice number
        /// and Date columns are displayed for TV and CR order types only. Vendor
        /// column is not available to JE and PC.
        /// </summary>
        /// <param name="orderTypeID">Order type for orders being displayed.</param>
        private void ManageMatchedGridDisplay(int orderTypeID)
        {
            switch ((OrderType) orderTypeID)
            {
                case OrderType.PurchaseOrder:
                    matchedOrdersDataGrid.Columns[(int) MatchedOrdersDataGridColumnIndex.InvoiceNumber].Visible =
                        matchedOrdersDataGrid.Columns[(int) MatchedOrdersDataGridColumnIndex.InvoiceDate].Visible = false;
                    matchedOrdersDataGrid.Columns[(int) MatchedOrdersDataGridColumnIndex.Vendor].Visible =
                        matchedOrdersDataGrid.Columns[(int) MatchedOrdersDataGridColumnIndex.Vendor].Visible = true;
                    break;

                case OrderType.JournalEntry:
                case OrderType.PettyCash:
                    matchedOrdersDataGrid.Columns[(int) MatchedOrdersDataGridColumnIndex.InvoiceNumber].Visible =
                        matchedOrdersDataGrid.Columns[(int) MatchedOrdersDataGridColumnIndex.InvoiceDate].Visible = false;
                    matchedOrdersDataGrid.Columns[(int) MatchedOrdersDataGridColumnIndex.Vendor].Visible =
                        matchedOrdersDataGrid.Columns[(int) MatchedOrdersDataGridColumnIndex.Vendor].Visible = false;
                    break;

                case OrderType.TravelVoucher:
                case OrderType.ChequeRequest:
                    matchedOrdersDataGrid.Columns[(int) MatchedOrdersDataGridColumnIndex.InvoiceNumber].Visible =
                        matchedOrdersDataGrid.Columns[(int) MatchedOrdersDataGridColumnIndex.InvoiceDate].Visible = true;
                    matchedOrdersDataGrid.Columns[(int) MatchedOrdersDataGridColumnIndex.Vendor].Visible =
                        matchedOrdersDataGrid.Columns[(int) MatchedOrdersDataGridColumnIndex.Vendor].Visible = true;
                    break;

                default:
                    break;
            }
        }

        /// <summary>
        /// This method sets the navigation url and the data to be passed for 
        /// when an item is clicked on matched orders data grid and peoplesoft data grid
        /// </summary>
        /// <param name="dictionary">hashtable to which data for navigation has to be added</param>
        /// <param name="cells">collection of table cells in data grid item</param>
        /// <param name="orderType">user order type being displayed in the particular row</param>
        /// <param name="orderNumberIndex">Index of the cell containing the order number information</param>
        /// <returns>Url of the Page to which user has to navigate</returns>
        private string ManageNavigation(Hashtable dictionary, TableCellCollection cells,
            OrderType orderType, int orderNumberIndex)
        {
            string navigationURL = null;

            switch (orderType)
            {
                case OrderType.PurchaseOrder:
                case OrderType.TravelVoucher:
                case OrderType.ChequeRequest:
                    dictionary.Add(Keys.POAPOrderNumberKey, ((LinkButton) cells[orderNumberIndex].
                        Controls[FirstChildControl]).Text);
                    dictionary.Add(Keys.POAPOrderTypeKey, (int) orderType);
                    navigationURL = Settings.GetKeyValue
                        ("SubNavigation.PurchasingDetails.PODetailsURL", string.Empty);
                    break;
                case OrderType.JournalEntry:
                case OrderType.PettyCash:
                    dictionary.Add(Keys.JournalIDKey, ((LinkButton) cells[orderNumberIndex].
                        Controls[FirstChildControl]).Text);
                    navigationURL = Settings.GetKeyValue
                        ("SubNavigation.PurchasingDetails.FinancialTransactionURL", string.Empty);
                    break;
                default:
                    break;
            }
            return navigationURL;
        }

        #endregion

        #region Web Form Designer generated code

        /// <summary>
        /// Handler for the event raised when the page is being initialized.
        /// Retrieve the cache policy in force.
        /// </summary>
        /// <param name="e">Argument associated with the event</param>
        protected override void OnInit(EventArgs e)
        {
            InitializeComponent();
            base.OnInit(e);

            cachePolicy = (Policy.ICachePolicy) Session[Policy.Names.CachePolicyKey];
            pageCachePolicy = (Policy.ICachePolicy) Session[Policy.Names.PageCachePolicyKey];

            this.insightDownloadBar.FailedAction += new CommandEventHandler(OnDownloadFailed);
        }

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.insightHeader.Navigate += new System.Web.UI.WebControls.CommandEventHandler(this.OnHeaderNavigate);
            this.purchasingSubNaviagation.Navigate += new System.Web.UI.WebControls.CommandEventHandler(this.OnSubNavigate);
            this.searchCriteriaDisplay.DisplayCriteria += new PHSRAG.Insight21.InsightWebControls.SearchCriteriaDisplay.CriteriaDisplayEventHandler(this.OnDisplayCriteria);
            this.breadCrumb.Navigate += new System.Web.UI.WebControls.CommandEventHandler(this.OnBreadCrumbNavigate);
            this.insightDataGrid.ItemCreated += new System.Web.UI.WebControls.DataGridItemEventHandler(this.OnInsightDataGridItemCreated);
            this.insightDataGrid.ItemCommand += new System.Web.UI.WebControls.DataGridCommandEventHandler(this.OnInsightDataGridItemCommand);
            this.insightDataGrid.SortCommand += new System.Web.UI.WebControls.DataGridSortCommandEventHandler(this.OnInsightDataGridSortCommand);
            this.insightDataGrid.ItemDataBound += new System.Web.UI.WebControls.DataGridItemEventHandler(this.OnInsightDataGridItemDataBound);
            this.peopleSoftDataGrid.ItemCreated += new System.Web.UI.WebControls.DataGridItemEventHandler(this.OnPeopleSoftDataGridItemCreated);
            this.peopleSoftDataGrid.ItemCommand += new System.Web.UI.WebControls.DataGridCommandEventHandler(this.OnPeopleSoftDataGridItemCommand);
            this.peopleSoftDataGrid.SortCommand += new System.Web.UI.WebControls.DataGridSortCommandEventHandler(this.OnPeopleSoftDataGridSortCommand);
            this.peopleSoftDataGrid.ItemDataBound += new System.Web.UI.WebControls.DataGridItemEventHandler(this.OnPeopleSoftDataGridItemDataBound);
            this.matchedOrdersDataGrid.ItemCreated += new System.Web.UI.WebControls.DataGridItemEventHandler(this.OnMatchedOrdersDataGridItemCreated);
            this.matchedOrdersDataGrid.ItemCommand += new System.Web.UI.WebControls.DataGridCommandEventHandler(this.OnMatchedOrdersDataGridItemCommand);
            this.matchedOrdersDataGrid.SortCommand += new System.Web.UI.WebControls.DataGridSortCommandEventHandler(this.OnMatchedOrdersDataGridSortCommand);
            this.matchedOrdersDataGrid.ItemDataBound += new System.Web.UI.WebControls.DataGridItemEventHandler(this.OnMatchedOrdersDataGridItemDataBound);
            this.Load += new System.EventHandler(this.OnPageLoad);
        }

        #endregion

        private void OnBreadCrumbNavigate(object sender, CommandEventArgs e)
        {
            PurgeCache();
        }

        private void OnSubNavigate(object sender, CommandEventArgs e)
        {
            PurgeCache();
        }

        public void PurgeCache()
        {
            if (unMatchedOrders != null)
            {
                unMatchedOrders.Purge();
                cachePolicy.UnCache(ReconcileUnMatchedOrders.CacheKey);
            }

            if (matchedOrders != null)
            {
                matchedOrders.Purge();
                cachePolicy.UnCache(ReconcileMatchedOrders.CacheKey);
            }
        }
    }
}